# encoding: utf-8
require 'travis/cli'

module Travis
  module CLI
    class Encrypt < RepoCommand
      description "encrypts values for the .travis.yml"
      attr_accessor :config_key

      on('-a', '--add [KEY]', 'adds it to .travis.yml under KEY (default: env.global)') do |c, value|
        c.config_key = value || 'env.global'
      end

      on('-s', '--[no-]split', "treat each line as a separate input")
      on('-p', '--append',     "don't override existing values, instead treat as list")
      on('-x', '--override',   "override existing value")

      def run(*args)
        error "cannot combine --override and --append"   if append?   and override?
        error "--append without --add makes no sense"    if append?   and not add?
        error "--override without --add makes no sense"  if override? and not add?
        self.override |= !config_key.start_with?('env.') if add?      and not append?

        if args.first =~ %r{\w+/\w+} && !args.first.include?("=")
          warn "WARNING: The name of the repository is now passed to the command with the -r option:"
          warn "    #{command("encrypt [...] -r #{args.first}")}"
          warn "  If you tried to pass the name of the repository as the first argument, you"
          warn "  probably won't get the results you wanted.\n"
        end

        data = args.join(" ")

        if data.empty?
          say color("Reading from stdin, press Ctrl+D when done", :info) if $stdin.tty?
          data = $stdin.read
        end

        data = split? ? data.split("\n") : [data.strip]
        warn_env_assignments(data)
        encrypted = data.map { |data| repository.encrypt(data) }

        if config_key
          set_config encrypted.map { |e| { 'secure' => e } }
          save_travis_config
        else
          list = encrypted.map { |data| format(data.inspect, "  secure: %s") }
          say(list.join("\n"), template(__FILE__), :none)
        end
      rescue OpenSSL::PKey::RSAError => error
        error "#{error.message.sub(" for key size", "")} - consider using "    <<
          color("travis encrypt-file", [:red, :bold]) <<
          color(" or ", :red)                         <<
          color("travis env set", [:red, :bold])
      end

      private

        def add?
          !!config_key
        end

        def set_config(result)
          parent_config[last_key] = merge_config(result)
        end

        def merge_config(result)
          case subconfig = (parent_config[last_key] unless override?)
          when nil   then result.size == 1 ? result.first : result
          when Array then subconfig + result
          else            result.unshift(subconfig)
          end
        end

        def subconfig
        end

        def key_chain
          @key_chain ||= config_key.split('.')
        end

        def last_key
          key_chain.last
        end

        def parent_config
          @parent_config ||= traverse_config(travis_config, *key_chain[0..-2])
        end

        def traverse_config(hash, key = nil, *rest)
          return hash unless key

          hash[key] = case value = hash[key]
                      when nil  then {}
                      when Hash then value
                      else { 'matrix' => Array(value) }
                      end

          traverse_config(hash[key], *rest)
        end

        def warn_env_assignments(data)
          if /env/.match(config_key) && data.find { |d| /=/.match(d).nil? }
            warn "Environment variables in #{config_key} should be formatted as FOO=bar"
          end
        end
    end
  end
end

__END__
Please add the following to your <[[ color('.travis.yml', :info) ]]> file:

%s

Pro Tip: You can add it automatically by running with <[[ color('--add', :info) ]]>.
